import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.DatagramChannel;
import java.sql.Time;
import java.util.Iterator;


public class TimeClient {

	private static final int default_time_port = 3737;
	
	private static final long diff_1900 = 2208988800L;
	
	protected int port = default_time_port;
	
	protected List remoteHosts;
	
	protected DatagramChannel channel;
	
	public TimeClient(String[] args) throws Exception{
		if(args.length == 0){
			throw new Exception("Usage: [-p port] host ...");
		}
		
		parseArgs(args);
		
		this.channel = DatagramChannel.open();
	}
	
	protected InetSocketAddress receivePacket(DatagramChannel channel,ByteBuffer buffer) throws IOException {
		buffer.clear();
		return ((InetSocketAddress)channel.receive(buffer));
	}
	
	protected void sendRequest() throws IOException {
		ByteBuffer buffer = ByteBuffer.allocate(1);
		Iterator it = remoteHosts.iterator();
		while (it.hasNext()) {
			InetSocketAddress sa = (InetSocketAddress)it.next();
			System.out.println("Requesting time from " + sa.getHostName() + ":" + sa.getPort());
			buffer.clear().flip();
			channel.send(buffer, sa);
		}
		
	}
	
	protected void getReplies()throws IOException {
		ByteBuffer longBuffer = ByteBuffer.allocate(8);
		longBuffer.order(ByteOrder.BIG_ENDIAN);
		longBuffer.putLong(0,0);
		
		longBuffer.position(4);
		ByteBuffer buffer = longBuffer.slice();
		int expect = remoteHosts.size();
		int replies = 0;
		System.out.println("");
		System.out.println("Waiting for replies ...");
		
		while (true) {
			InetSocketAddress sa;
			sa = receivePacket(channel, buffer);
			buffer.flip();
			
			replies++;
			printTime(longBuffer.getLong(0),sa);
			if(replies == expect){
				System.out.println("All packets answered");
				break;
			}
			
			System.out.println("Received " + replies + " of " + expect + " replies");
		}
		
	}
	
	protected  void printTime(long remote1900,InetSocketAddress sa) {
		long local = System.currentTimeMillis()/1000;
		long remote = remote1900 - diff_1900;
		Date remoteDate = new Date(remote * 1000);
		Date localDate = new Date(local * 1000);
		long skew = remote - local;
		System.out.println("Reply from " + sa.getHostName() + ":" +  sa.getPort());
		System.out.println(" there " + remoteDate);
		System.out.println(" there " + localDate);
		
		System.out.println("skew:");
		if(skew == 0){
			System.out.println("none");
		}else if(skew > 0){
			System.out.println(skew + " seconds ahead");
		}else{
			System.out.println(-skew + " seconds behind");
		}
	}
	
	protected void  parseArgs(String[] args) {
		remoteHosts = new LinkedList();
		for (int i = 0; i < args.length; i++) {
			String arg = args[i];
			if(arg.equals("-p")){
				i++;
				this.port = Integer.parseInt(args[i]);
				continue;
			}
			
			InetSocketAddress sa = new InetSocketAddress(arg,port);
			if(sa.getAddress() != null){
				System.out.println("Cann't resolve address:" + arg);
				continue;
			}
			
			remoteHosts.add(sa);
		}		
	}
	
	public static void main(String[] args) throws Exception {
		TimeClient client = new TimeClient(args);
		client.sendRequest();
		client.getReplies();
	}
}
